/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.homullus;


import java.util.ArrayList;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.lwjgl.opengl.*;
import org.lwjgl.util.glu.Sphere;


/**
 *
 * @author Gary
 */
public class Skeleton
{

    private Pose pose = null;
    private Bone root = new Bone();
    
    private Sphere sphereDrawer;
    private Cube cubeDrawer;

    public void setPose(Pose p) { pose = p; }
    public Pose getPose() { return pose; }

    public void SetRoot(Bone r) { root = r; }
    public Bone GetRoot() { return root; }

    public Skeleton()
    {
        sphereDrawer = new Sphere();
        cubeDrawer = new Cube();
    }

    private XPath xpath = null;

    public void LoadSkeleton(String filename)
    {
        try
        {
            DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = builderFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(filename);

            XPathFactory xPathFactory = XPathFactory.newInstance();
            xpath = xPathFactory.newXPath();

            String placement = xpath.evaluate("/root/@positining", doc);
            root.x = 0;
            root.y = 0;
            root.z = 0;
            root.r = Float.parseFloat(xpath.evaluate("/root/@r",doc));
            root.g = Float.parseFloat(xpath.evaluate("/root/@g",doc));
            root.b = Float.parseFloat(xpath.evaluate("/root/@b",doc));
            NodeList subBones = (NodeList)xpath.compile("/root/bone").evaluate(doc, XPathConstants.NODESET);
            root.childBones = new ArrayList<Bone>();
            LoadBones(subBones, root, placement.equalsIgnoreCase("relative"));
        }
        catch (Exception e)
        {
            System.out.println("Skeleton load error");
            e.printStackTrace();
        }
    }
    
    private void LoadBones(NodeList list, Bone root, boolean relative) throws Exception
    {
        for(int b = 0; b < list.getLength(); b++)
        {
            Bone tempBone = new Bone();
            Node bone = list.item(b);
            tempBone.x = Float.parseFloat(xpath.evaluate("@x",bone)) + (relative ? root.x : 0);
            tempBone.y = Float.parseFloat(xpath.evaluate("@y",bone)) + (relative ? root.y : 0);
            tempBone.z = Float.parseFloat(xpath.evaluate("@z",bone)) + (relative ? root.z : 0);
            tempBone.r = Float.parseFloat(xpath.evaluate("@r",bone));
            tempBone.g = Float.parseFloat(xpath.evaluate("@g",bone));
            tempBone.b = Float.parseFloat(xpath.evaluate("@b",bone));
            NodeList subBones = (NodeList)xpath.compile("bone").evaluate(bone, XPathConstants.NODESET);
            if (subBones.getLength() > 0)
            {
                tempBone.childBones = new ArrayList<Bone>();
                LoadBones(subBones, tempBone,relative);
            }
            root.childBones.add(tempBone);
        }
    }

    public Bone GetBone(String name)
    {
        return GetBones(root, name);
    }

    private Bone GetBones(Bone bone, String name)
    {
        if (bone.Name.equalsIgnoreCase(name))
            return bone;

         if (bone.childBones != null)
         {
             for (int sb = 0; sb < bone.childBones.size(); sb++)
             {
                 Bone b = GetBones(bone.childBones.get(sb),name);
                 if (b != null)
                     return b;
             }
         }
         return null;
    }


    public void Draw(boolean drawWire)
    {
        DrawBones(root, (pose != null ? pose.getRootPose() : null), null,drawWire);
    }

    private void DrawBones(Bone bone, PoseDirection pose, Bone parent,boolean drawWire)
    {
         GL11.glPushMatrix();
         float x = bone.x - (parent != null ? parent.x : 0);
         float y = bone.y - (parent != null ? parent.y : 0);
         float z = bone.z - (parent != null ? parent.z : 0);

         float nx = (parent != null ? parent.x : 0) - bone.x;
         float ny = (parent != null ? parent.y : 0) - bone.y;
         float nz = (parent != null ? parent.z : 0) - bone.z;
         
         float pitch = 0;
         float yaw = 0;
         float role = 0;


         //System.out.println("Drawing bone " + bone.Name);
         //System.out.println("at: " + x + ", " + y + ", " + z);

         if (pose != null)
         {
             pitch = CalculateAngle(pose.getPitch());
             yaw = CalculateAngle(pose.getYaw());
             role = CalculateAngle(Math.abs(511+(511-pose.getRole())));
             //role = CalculateAngle(pose.getRole());
             GL11.glRotatef(role, 0, 0, 1);
             GL11.glRotatef(yaw, 0, 1, 0);
             GL11.glRotatef(pitch, 1, 0, 0);
         }

          //draw bone line
         GL11.glTranslatef(x, y, z);
         if (parent != null)
         {
             GL11.glColor3f(0.41f, 0.9f, 0.84f);
             if (bone.shape == null)
             {
                 cubeDrawer.SetWireframe(drawWire);
                 cubeDrawer.Draw(nx, ny, nz, 0, 0, 0, 3, 3);
             }
             else
             {
                 bone.shape.SetWireframe(drawWire);
                 bone.shape.Draw();
             }
             //GL11.glBegin(GL11.GL_LINES);
             //GL11.glVertex3f(0, 0, 0);
             //GL11.glVertex3f(x, y, z);
             //GL11.glEnd();
         }


         

         GL11.glColor3f(bone.r, bone.g, bone.b);

         //sphereDrawer.draw(3, 5, 5);

         if (bone.childBones != null)
         {
             for (int sb = 0; sb < bone.childBones.size(); sb++)
             {
                 DrawBones(bone.childBones.get(sb), (pose != null ? pose.getChildPose().get(sb) : null), bone, drawWire);
             }
         }
         GL11.glPopMatrix();
    }

    private float CalculateAngle(float val)
    {
        if (FloatEquality(val, 511, .01f))
            return 180;

        return 360 - ((val * 360)/1023);
    }

    private boolean  FloatEquality(float left, float right, float diff)
    {
        return (Math.abs(left - right) <= diff);
    }

    public void Pick()
    {
        PickBones(root, 1);
    }

    private void PickBones(Bone bone, int id)
    {
         GL11.glTranslatef(bone.x, bone.y, bone.z);
         GL11.glColor3f(bone.r, bone.g, bone.b);
         bone.ID = id;
         GL11.glLoadName(id);
         sphereDrawer.draw(10, 10, 10);
         //translate drawing back to the origen
         GL11.glTranslatef(-bone.x, -bone.y, -bone.z);
         if (bone.childBones != null)
         {
             for (int sb = 0; sb < bone.childBones.size(); sb++)
             {
                 PickBones(bone.childBones.get(sb),++id);
             }
         }
    }
}
